########################################################################
#  __  __    _      _     __  __   __   _  _  ___  ___  __  __    ___  #
# (  )(  ) _( )_  _( )_  (  \/  ) (  ) ( )/ )(  _)(  _)(  )(  )  (  _) #
#  )(__)( (_   _)(_   _)  )    (  /__\  )  \  ) _) ) _) )(  )(__  ) _) #
#  \____/   (_)    (_)   (_/\/\_)(_)(_)(_)\_)(___)(_)  (__)(____)(___) #
#                                                                      #
############################## USAGE ###################################
# This makefile is controlled through a set of variables, similar to   #
# any other makefile. The prefered way to change them is via command   #
# line, e.g.: make PKG="ide usvn" CXX=g++                              #
#                                                                      #
# All of the variables that can control the bahaviour are listed below #
# in DEFAULTS section. Also their default value is shown here. Notice  #
# that many of them can contain shell commands (enclosed in ``) which  #
# are evaluated before parsing and building each package.
#                                                                      #
# For boolean type of variables value "1" represents true, anything    #
# else is evaluated as false.                                          #
############################# TARGETS ##################################
# This makefile defines five targets that are meant to be used         #
# directly by user from the command line:                              #
#                                                                      #
# all          - builds all (default if any packages are specified)    #
# help         - shows this help (default if no packages are given)    #
# simulate     - prints all commands, without executing them           #
# clean        - deletes all compiled files related to given packages  #
# dist-clean   - deletes the entire OUT directory (including parsers)  #
############################# EXAMPLES #################################
# Typical usage:                                                       #
#    make PKG=ide FLAGS="GCC"                                          #
# More complicated usage:                                              #
#    make PKG="ide UWord Bombs" FLAGS="GCC .NOGTK" ECHO=1              #
# Show help:                                                           #
#    make help                                                         #
# Silent mode:                                                         #
#    make PKG=ide SILENT=1                                             #
############################# DEFAULTS #################################
# List of packages to build (space separated). If empty, this help     #
# text will be shown.                                                  #
PKG:=
# Paths where to look for packages.                                    #
NESTS:=uppsrc bazaar examples reference tutorial $$HOME/MyApps
# Building flags (as in TheIDE).
FLAGS:=GCC
# Additional include paths for compiler (without the leading "-I").    #
INCPATHS:= /usr/include/freetype2 /usr/include/gtk-2.0
INCPATHS+= /usr/local/include/gtk-2.0 /usr/include/glib-2.0
INCPATHS+= /usr/local/include/glib-2.0 /usr/lib/glib-2.0/include
INCPATHS+= /usr/local/lib/glib-2.0/include /usr/lib/gtk-2.0/include
INCPATHS+= /usr/local/lib/gtk-2.0/include /usr/include/cairo
INCPATHS+= /usr/include/pango-1.0 /usr/include/atk-1.0
INCPATHS+= /usr/X11R6/include /usr/X11R6/include/freetype2
INCPATHS+= /usr/X11R6/include/gtk-2.0 /usr/X11R6/include/glib-2.0
INCPATHS+= /usr/X11R6/lib/glib-2.0/include
INCPATHS+= /usr/X11R6/lib/gtk-2.0/include /usr/X11R6/include/cairo
INCPATHS+= /usr/X11R6/include/pango-1.0 /usr/X11R6/include/atk-1.0
INCPATHS+= /usr/local/include/cairo /usr/local/include/pango-1.0
INCPATHS+= /usr/local/include/atk-1.0 /usr/local/include
INCPATHS+= /usr/local/include/libpng /usr/include/gdk-pixbuf-2.0
INCPATHS+= /usr/lib/i386-linux-gnu/glib-2.0/include
INCPATHS+= /usr/lib/x86_64-linux-gnu/glib-2.0/include
INCPATHS+= /usr/lib/i386-linux-gnu/gtk-2.0/include
INCPATHS+= /usr/lib/x86_64-linux-gnu/gtk-2.0/include
INCPATHS+= /usr/lib/gtk-2.0/include
INCPATHS+= /usr/lib/glib-2.0/include
# Paths to libraries for linker.                                       #
LIBPATHS:=/usr/X11R6/lib /usr/lib /usr/local/lib
# Directory to store intermediate object files                         #
OUT:=_out
# Directory where the resulting binaries will be stored                #
BIN:=$(OUT)/bin
# ar command                                                           #
AR:=ar -src
# Command to create directories equivalent to "mkdir -p". The only     #
# good reason to change is if your platform uses mkdir command which   #
# doesn't support the -p option                                        #
MKDIRP:=mkdir -p
# C compiler command                                                   #
CC:=cc
# C++ compiler command                                                 #
CXX:=c++
# Options for C/C++ preprocessor                                       #
CPPFLAGS:=
# Options for C compiler                                               #
CFLAGS:=
# Options for C++ compiler                                             #
CXXFLAGS:=
# Options for linker                                                   #
LDFLAGS:=-Wl,--gc-sections -Wl,-s -Wl,-O,2
# Additional options for speed optimization                            #
SPEEDFLAGS:=-O3 -ffunction-sections -fdata-sections
# Additional options for size optimization                             #
SIZEFLAGS:=-Os -finline-limit=20 -ffunction-sections -fdata-sections
# Optimization, supply SPEED or SIZE to optimize the executables       #
OPT:=
# Platform flag, added to FLAGS, can include shell commands            #
PLATFORM:=`uname | tr a-z A-Z` POSIX
# Suppress all messagges                                               #
SILENT:=0
# Print each executed command                                          #
ECHO:=0
# Use dependency files. This will probably work only with gcc-like     #
# compilers (requires -MD option). The build usually works correctly   #
# even without specifying the dependencies unless you modify included  #
# files (*.h, *.lay, ...) without modifying the *.c/cpp files that     #
# include them.                                                        # 
DEPS:=0
# Run the executable(s) sequntially when compiled                      #
RUN:=0
# Override default name of final executable (absolute or relative path)
TARGET:=
# Add flags from based on mainconfig section (1-based index, set       #
# to 0 to disable completely                                           #
USEMAINCFG:=1
# Number of parallel processes to use (like "make -jN" option).        #
# The JOBS variable must be used instead of -j option (as -j would not #
# work as expected). Default value is the number of cores (if it is    #
# possible to detect) or 1.                                            #
JOBS:=$(shell echo /sys/devices/system/cpu/cpu[0-9] | wc -w || echo 1)
# Ask for some additional details during parsing                       #
INTERACTIVE:=0
# Use colorized output, recognized values are 0, 1 or auto             #
COLOR:=auto
# What color should be used in colorized mode, number from 0 to 7      #
HIGHLIGHT:=4
TIME:=TIMEFORMAT="  time taken: %Es"; time
########################################################################

m:=[ "$(SILENT)" = "1" ] || echo
e:=@    #comment out for debugging
dbgparser:=-DflagDEBUG -ggdb3

thisfile:=$(MAKEFILE_LIST)$(.MAKEFILE_LIST)
color:=if [ "$(COLOR)" = "auto" ]; then [ -t 1 ] && c=1 || c=0; else c="$(COLOR)"; fi
pass:=$(color); UPP_NESTS="$(NESTS)" UPP_FLAGS="$(FLAGS)" \
    UPP_INCPATHS="$(INCPATHS)" UPP_LIBPATHS="$(LIBPATHS)" UPP_OUT="$(OUT)" \
    UPP_BIN="$(BIN)" UPP_AR="$(AR)" UPP_MKDIRP="$(MKDIRP)" UPP_CC="$(CC)" \
    UPP_CXX="$(CXX)" UPP_CFLAGS="$(CFLAGS)" UPP_CXXFLAGS="$(CXXFLAGS)" \
    UPP_LDFLAGS="$(LDFLAGS)" UPP_SPEEDFLAGS="$(SPEEDFLAGS)" \
    UPP_SIZEFLAGS="$(SIZEFLAGS)" UPP_PLATFORM="$(PLATFORM)" \
    UPP_SILENT="$(SILENT)" UPP_ECHO="$(ECHO)" UPP_DEPS="$(DEPS)" \
    UPP_RUN="$(RUN)" UPP_TARGET="$(TARGET)" UPP_USEMAINCFG="$(USEMAINCFG)" \
    UPP_OPT="$(OPT)" UPP_INTERACTIVE="$(INTERACTIVE)" UPP_COLOR="$$c" \
    UPP_HIGHLIGHT="$(HIGHLIGHT)"

all: no-package-specified build

do-build: $(OUT)/parser $(OUT)/brc.sh $(OUT)/color.sh
	+$e set -e; $(color); B=""; N=""; C=""; color=""; \
	[ $$c -eq 1 ] && color="$(SHELL) $(OUT)/color.sh" && B="\e[1m" && N="\e[0m" && C="\e[1;3$(HIGHLIGHT)m"; \
	for pkg in $(PKG) ; do \
		$m $${B}Parsing package $$C$$pkg$$N $$B...$$N; \
		$(TIME) $(pass) $$color $(OUT)/parser $$pkg; \
		$m $${B}Building package$$N $$C$$pkg$$N $$B...$$N; \
		$(TIME) $$color $(MAKE) -j $(JOBS) -f $(OUT)/Makefile && \
		$m $${B}Package$$N $$C$$pkg$$N$$B finished ...$$N; \
	done; set +e

no-package-specified:
	$e if [ -z "$(PKG)" ]; then sed '/^$$/{s/.*//;q;}' $(thisfile) && false; fi

$(OUT)/parser: $(OUT)/parser.cpp
	@$m Compiling parser...
	+$e $(TIME) $(CXX) -x c++ -o $@ $^ $(dbgparser)

$(OUT)/parser.cpp: $(thisfile)
	@$m "Extracting parser..."
	+$e [ -d $(OUT) ] || $(MKDIRP) $(OUT)
	+$e $(TIME) sed -n 's/^#://p;' $< | base64 -d | gzip -d > $@

$(OUT)/brc.sh: $(thisfile)
	@$m "Extracting brc parser..."
	+$e [ -d $(OUT) ] || $(MKDIRP) $(OUT)
	+$e $(TIME) sed -n 's/^#~//p;' $< | base64 -d | gzip -d > $@

$(OUT)/color.sh: $(thisfile)
	@$m "Extracting colorizer script..."
	+$e [ -d $(OUT) ] || $(MKDIRP) $(OUT)
	+$e $(TIME) sed -n 's/^#%//p;' $< | base64 -d | gzip -d > $@

build: do-build
	$e for pkg in $(PKG); do \
		if [ "$(RUN)" = "1" ]; then \
			for target in "$(TARGET)" "$(BIN)/$(TARGET)" "$(BIN)/$$pkg"; do \
				[ -f "$$target" ] && break; \
			done; \
			[ -e "$$target" ] && { $m "Running $$target"; } && $$target; \
		fi; \
	done

simulate: $(OUT)/Makefile
	+$e for pkg in $(PKG) ; do \
		$m Parsing package $$pkg ... && \
		$(pass) $(OUT)/parser $$pkg && \
		$m Building package $$pkg ... && \
		$(MAKE) -nf $(OUT)/Makefile; \
	done

help:
	$e sed '/^$$/{s/.*//;q;}' $(thisfile);

clean:
	+$e for pkg in $(PKG); do \
		$m Parsing package $$pkg... && \
		$(pass) $(OUT)/parser $$pkg && \
		$m Cleaning package $$pkg && \
		$(MAKE) -f $(OUT)/Makefile clean; \
	done

dist-clean:
	@$m Deleting $(OUT) ...
	$e rm -rf $(OUT)

parser-clean:
	@$m Deleting parser files ...
	$e rm -f $(OUT)/parser $(OUT)/parser.cpp

.SUFFIXES:

.PHONY: all help simulate clean dist-clean parser-clean \
        build do-build no-package-specified

